using System;
using System.Collections;
using UnityEngine;

public class Entity : MonoBehaviour
{
    public event Action OnFlipped;
    public CharacterStats Stats { get; protected set; }
    public Rigidbody2D Rb { get; protected set; }
    public Animator Anim { get; protected set; }
    public EntityFX Fx { get; protected set; }
    public CapsuleCollider2D Collider { get; protected set; }
    // [Header("Collision info")]
    public Transform AttackPoint { get; private set; }
    public float attackCheckRadius = 1.2f;
    protected Transform groundCheck;
    protected LayerMask groundLayer;
    [SerializeField] protected float groundCheckDistance = 0.5f;
    protected Transform wallCheck;
    protected LayerMask wallLayer;
    [SerializeField] protected float wallCheckDistance = 0.5f;
    [Header("Knockback info")]
    [SerializeField] protected Vector2 knockbackPower = new(5, 5);
    [SerializeField] protected Vector2 knockbackOffset = new(0.5f, 2f);
    [SerializeField] protected float knockbackDuration = 0.07f;
    protected bool isKnockbacked;
    protected int knockbackDirection;
    public int FacingDirection { get; private set; } = 1;
    protected bool facingRight = true;

    protected virtual void Awake()
    {
        Stats = GetComponent<CharacterStats>();
        Rb = GetComponent<Rigidbody2D>();
        Anim = GetComponentInChildren<Animator>();
        Fx = GetComponent<EntityFX>();
        Collider = GetComponent<CapsuleCollider2D>();
        groundLayer = LayerMask.GetMask("Ground");
        wallLayer = LayerMask.GetMask("Ground");
        groundCheck = transform.Find("GroundCheck");
        wallCheck = transform.Find("WallCheck");
        AttackPoint = transform.Find("AttackPoint");
    }

    public virtual void SlowEntityBy(float slowPercentage, float slowDuration) { }

    protected virtual void ReturnDefaultSpeed()
    {
        const float targetSpeed = 1f;
        const float speedChangeRate = 5f;
        Anim.speed = Mathf.Lerp(Anim.speed, targetSpeed, Time.deltaTime * speedChangeRate);
    }

    public void DamageImpact() => StartCoroutine(HitKnockback());

    public void SetupKnockbackDirection(Vector2 position)
    {
        // 在右边
        if (position.x > transform.position.x)
        {
            knockbackDirection = -1;
        }
        else if (position.x < transform.position.x)
        {
            knockbackDirection = 1;
        }
    }

    private IEnumerator HitKnockback()
    {
        isKnockbacked = true;
        float xOffset = UnityEngine.Random.Range(knockbackOffset.x, knockbackOffset.y);
        // if (knockbackPower.x > 0 || knockbackPower.y > 0) // this line makes player immune to freeze effect when he takes hit
        Rb.velocity = new Vector2((knockbackPower.x + xOffset) * knockbackDirection, knockbackPower.y);
        yield return new WaitForSeconds(knockbackDuration);
        isKnockbacked = false;
        SetupZeroKnockbackPower();
    }

    public void SetVelocityToZero()
    {
        if (isKnockbacked) return;
        Rb.velocity = new Vector2(0, 0);
    }

    public void SetVelocity(float xVelocity, float yVelocity)
    {
        if (isKnockbacked) return;
        Rb.velocity = new Vector2(xVelocity, yVelocity);
        FlipController(xVelocity);
    }

    private void FlipController(float x)
    {
        if (x == 0 || isKnockbacked) return;
        if (x > 0 && !facingRight || x < 0 && facingRight) Flip();
    }

    public void Flip()
    {
        FacingDirection = -FacingDirection;
        facingRight = !facingRight;
        transform.Rotate(0, 180, 0);
        OnFlipped?.Invoke();
    }

    public virtual void Die() { }

    public bool IsGroundDetected() => Physics2D.OverlapCircle(groundCheck.position, groundCheckDistance, groundLayer);

    public bool IsWallDetected() => Physics2D.OverlapCircle(wallCheck.position, wallCheckDistance, wallLayer);

    public void SetupKnockbackPower(Vector2 newKnockbackPower)
    {
        knockbackPower = newKnockbackPower;
    }

    public void SetupKnockbackPower(float x, float y)
    {
        knockbackPower = new Vector2(x, y);
    }

    private void SetupZeroKnockbackPower()
    {
        knockbackPower = Vector2.zero;
    }

#if UNITY_EDITOR
    protected virtual void OnValidate()
    {
        groundCheck = transform.Find("GroundCheck");
        wallCheck = transform.Find("WallCheck");
        AttackPoint = transform.Find("AttackPoint");
    }
    protected virtual void OnDrawGizmos()
    {
        Gizmos.DrawLine(groundCheck.position, new Vector2(groundCheck.position.x, groundCheck.position.y - groundCheckDistance));
        Gizmos.DrawLine(wallCheck.position,
            new Vector2(wallCheck.position.x + wallCheckDistance * FacingDirection, wallCheck.position.y));
        Gizmos.DrawWireSphere(AttackPoint.position, attackCheckRadius);
    }
#endif
}